<template>
  <el-table
    v-bind="$attrs"
    :data="data"
    :height="height"
    :row-key="rowKey"
    ref="tableRef"
    @selection-change="selectionChange"
    @select="select"
    @select-all="selectAll"
  >
    <slot name="tableConfigListBefore" v-bind="plusEventsFirstParam"></slot>
    <TableItem
      v-for="(item,index) in tableConfigListCom || []"
      :key="'table' + index"
      :item="item"
      :table-vm="self"
      :tableData="data"
    ></TableItem>
    <template #append>
      <slot name="append" v-bind="plusEventsFirstParam"></slot>
    </template>
    <template #empty>
      <slot name="empty" v-bind="plusEventsFirstParam"></slot>
    </template>
    <slot v-bind="plusEventsFirstParam"></slot>
  </el-table>
</template>

<script lang="ts">
import {defineComponent} from 'vue'
import {PropType} from "vue";
import {DisabledComFunc, TableConfigItem, TableEventFirstParam} from './types.ts'
import TableItem from './TableItem.vue'
//     "sortablejs": "^1.15.0",     "@types/sortablejs": "^1.15.3",
import Sortable from "sortablejs";
import {TreeUtils} from "../../utils/TreeUtils";
import typeOfUtils from "../../utils/type-of-utils";

export default defineComponent({
  name: 'DynamicTable',
  components: {
    TableItem,
  },
  props: {
    data: {
      type: Array,
      default: []
    },
    tableConfigList: {
      type: Array as PropType<TableConfigItem[]>
    },
    height: {
      type: [String, Number],
      default: 'auto'
    },
    disabledCom: Function as PropType<DisabledComFunc>, // 禁用组件的函数
    createdInitValue: { // 创建组件的时候初始化组件数据
      type: Boolean,
      default: true
    },
    rowKey:{
      type: [String,Function] as import("vue").PropType<string | ((row: any) => string) | undefined>,
    },
    rowDragSort:{ // 行排序，一定要放row-key，不然没用
      type: Boolean,
      default: false,
    },
    parentId: [String,Number], // 如果是树形结构一定要写父级id，目前不支持
  },
  computed: {
    self() {
      return this
    },
    plusEventsFirstParam():TableEventFirstParam {
      return {
        tableData: this.data,
        oldSelectChange: this.oldSelectChange,
        addSelect: this.addSelect,
        removeSelect: this.removeSelect,
        notChangeSelect: this.notChangeSelect,
        nowSelect: this.nowSelect,
      }
    },
    /**
     * 用于继承扩展
     * 计算hidden的item进行移除
     */
    tableConfigListCom() {
      return TreeUtils.treeFilter(this.tableConfigList || [] as TableConfigItem[],(item, parent, index)=>{
        // 支持hidden为函数
        // @ts-ignore
        if(typeOfUtils.isFunction(item.hidden)) {
          // @ts-ignore
          return !item.hidden(item)
        }
        return !(item.hidden || false)
      }) as TableConfigItem[]
    }
  },
  emits:[
    'rowSortableEnd',
    'selectionChange',
    'select',
    'selectAll',
    'selectChangeEvent', // 和这个selectionChange是有区别的，只有点击checkbox/radio才会触发，也是自己额外加的
    'rowUpdateEvent', // 行数据更新时触发，也是自己额外加的
  ],
  data() {
    return {
      rowSortable: null as Sortable,
      oldSelectChange: [],
      addSelect: [],
      removeSelect: [],
      notChangeSelect: [],
      nowSelect: [],
    }
  },
  watch: {
    rowDragSort: {
      immediate: true,
      handler() {
        this.$nextTick(()=>{
          if(this.rowDragSort) {
            if(!this.rowSortable) {
              // 通过ref获取Dom节点
              const el = this.$refs.tableRef.$el.querySelectorAll(
                ".el-table__body-wrapper table tbody"
              )[0];
              this.rowSortable = Sortable.create(el, {
                animation: 600, //拖拽动画(毫秒)
                // 结束拖拽
                onEnd: (e) => {
                  if(this.parentId) {
                    // 需要后续开发
                  } else {
                    const targetRow = this.data.splice(e.oldIndex, 1)[0]
                    this.data.splice(e.newIndex, 0, targetRow)
                  }
                  this.$emit('rowSortableEnd')
                },
              });
            }
          } else {
            this.rowSortableDestroy()
          }
        })
      }
    }
  },
  methods: {
    rowSortableDestroy(){
      if(this.rowSortable) {
        this.rowSortable.destroy()
        this.rowSortable=null
      }
    },
    countAddAndRemove(nowSelect = [], oldSelect = []) {
      this.nowSelect = nowSelect
      this.addSelect = nowSelect.filter(n => {
        const findIndex = oldSelect.findIndex(o => o === n)
        return findIndex === -1
      })
      this.removeSelect = oldSelect.filter(o => nowSelect.findIndex(n => n === o) === -1)
      this.notChangeSelect = nowSelect.filter(n => oldSelect.findIndex(o => o === n) > -1)
    },
    /**
     * 当用户手动勾选数据行的 Checkbox 时触发的事件
     * select----selectionChange
     * @param selection
     * @param row
     */
    select(selection, row) {
      this.$nextTick(()=>{
        // 控制select在selectionChange以后往外提交
        this.$emit('select', this.plusEventsFirstParam, selection, row)
        this.$emit('selectChangeEvent', this.plusEventsFirstParam, selection)
      })
    },
    /**
     * 当用户手动勾选全选 Checkbox 时触发的事件
     * selectionChange-----selectAll
     * @param selection
     */
    selectAll(selection) {
      this.$emit('selectAll', this.plusEventsFirstParam, selection)
      this.$emit('selectChangeEvent', this.plusEventsFirstParam, selection)
    },
    /**
     * 当选择项发生变化时会触发该事件
     * @param selection
     */
    selectionChange(selection = []) {
      this.countAddAndRemove(selection,this.oldSelectChange)
      this.$emit('selectionChange', this.plusEventsFirstParam, selection)
      this.oldSelectChange = selection
    },
    /**
     * 用于单选表格，设定某一行为选中行， 如果调用时不加参数，则会取消目前高亮行的选中状态。
     * @param row
     */
    setCurrentRow(row) {
      this.$refs.tableRef.setCurrentRow(row)
    },
    /**
     * 用于多选表格，切换某一行的选中状态， 如果使用了第二个参数，则可直接设置这一行选中与否
     * @param row
     * @param selected
     */
    toggleRowSelection(row, selected) {
      this.$refs.tableRef.toggleRowSelection(row, selected)
    },
    /**
     * 	用于多选表格，清空用户的选择
     */
    clearSelection() {
      return this.$refs.tableRef.clearSelection()
    },
    /**
     * 	返回当前选中的行
     */
    getSelectionRows() {
      return this.$refs.tableRef.getSelectionRows()
    },
  },
  beforeUnmount() {
    this.rowSortableDestroy()
  }
})
</script>

<style lang="scss" scoped>

</style>
